#include "graph_partition.h"

void graph_partition::dag_to_hgr(class ff_graph& ff_obj, char* hgr_file)
{
    FILE *fp_hgr = fopen (hgr_file, "w");

    if (fp_hgr == NULL) 
    {
        perror ("Error opening hgr file");
    }

    rewind(fp_hgr);

    //hgr start as edge num and vertex num
    fprintf(fp_hgr,"%d %d\n", ff_obj.get_edge_num(), ff_obj.get_vertex_num());

    //hgr vertex index start at 1
    for (unsigned int i = 0; i < ff_obj.get_edge_num(); i++)
    {
        fprintf(fp_hgr,"%d %d\n", 
            ff_obj.edge_set[i].in + 1, ff_obj.edge_set[i].out + 1);
    }
    
    fclose(fp_hgr);
}

void graph_partition::partition_to_fix(class ff_graph& ff_obj, 
                                char* hgr_file, char* fix_file)
{
    FILE *fp_fix = fopen (fix_file, "w");
    FILE *fp_hgr = fopen (hgr_file, "r");

    if (fp_fix == NULL) 
    {
        perror ("Error opening fix file");
    }

    if (fp_hgr == NULL) 
    {
        perror ("Error opening hgr.100 file");
    }

    rewind(fp_fix);
    rewind(fp_hgr);

    color_num = 0;
    unsigned int vertex = 0;

    vector <unsigned int> vertex_partition;
    vector <unsigned int> black_vertex;

    //all gates are white initially
    ff_obj.initial_vertex_color();

    while (!feof (fp_hgr)) 
    {
        int part_num;
        fscanf(fp_hgr,"%d\n", &part_num);
        vertex_partition.push_back(part_num);
        if (part_num < 10 && part_num > 0 && part_num % 1 == 0 || part_num == 20)
        {
            black_vertex.push_back(vertex);
        }
        vertex++;
    }

    assert (vertex_partition.size() == ff_obj.get_vertex_num());

    vector<unsigned int>::iterator black_vertex_it;
    for (black_vertex_it = black_vertex.begin(); 
            black_vertex_it != black_vertex.end(); black_vertex_it++)
    {
        unsigned int v = *black_vertex_it;
        vector<edge>::iterator edge_it;
        for (edge_it = ff_obj.vertex_fanin[v].begin(); 
            edge_it != ff_obj.vertex_fanin[v].end(); ++edge_it)
        {
            unsigned int source = (*edge_it).in;

            if (ff_obj.get_vertex_color(source) == 0) //white
            {
                ff_obj.set_vertex_color(source, 1); // set src vertex gray 
                vertex_partition[source] = vertex_partition[v];
            }
        }

        for (edge_it = ff_obj.vertex_fanout[v].begin(); 
            edge_it != ff_obj.vertex_fanout[v].end(); ++edge_it)
        {
            unsigned int sink = (*edge_it).out;

            if (ff_obj.get_vertex_color(sink) == 0) //white
            {
                ff_obj.set_vertex_color(sink, 1); // set sink vertex gray 
                vertex_partition[sink] = vertex_partition[v];
            }
        }
    }

    //ignore source and sink
    fprintf(fp_fix,"%d\n", -1);
    for (unsigned int i = 1; i < ff_obj.get_vertex_num() - 1; i++)
    {
        if (vertex_partition[i] < 10 && vertex_partition[i] > 0 )
        {
            fprintf(fp_fix,"%d\n", vertex_partition[i] - 1);
            color_num++;
        }
        else if (vertex_partition[i] == 20)
        {
            fprintf(fp_fix,"%d\n", 9);
            color_num++;
        }
        else
        {
            fprintf(fp_fix,"%d\n", -1);
        }
    }
    fprintf(fp_fix,"%d\n", -1);
    
    fclose(fp_fix);
    fclose(fp_hgr);
}

void graph_partition::gen_gray_reach_list(class ff_graph& ff_obj)
{
    prime_path_num = 0;
    gray_reach_list.clear();
    full_reach_list.clear();

    // remove edges with two gray vertexes
    vector <unsigned int> remove_tag; 
    remove_tag.assign(ff_obj.get_edge_num(), 0);

    for (unsigned int i = 0; i < ff_obj.get_edge_num(); i++)
    {
        struct edge e = ff_obj.edge_set[i];
        if (ff_obj.get_vertex_color(e.in) == 1 
            && ff_obj.get_vertex_color(e.out) == 1)
        {
            remove_tag[i] = 1; //delete;
        }
    }

    //temp store the edge set
    temp_edge_set = ff_obj.edge_set;

    ff_obj.edge_set.clear();

    for (unsigned int i = 0; i < remove_tag.size(); i++)
    {
        if (remove_tag[i] == 0)
        {
            ff_obj.edge_set.push_back(temp_edge_set[i]);
        }
    }

    ff_obj.set_edge_num(ff_obj.edge_set.size());
    ff_obj.gen_fanins();
    ff_obj.gen_fanouts();

    //set<unsigned int >::iterator gray_set_it;
    vector<unsigned int >::iterator gray_set_it;
    for (gray_set_it = gray_set.begin(); 
            gray_set_it != gray_set.end(); gray_set_it++) 
    {
        //set<unsigned int> current_gray_reach_list;
        //current_gray_reach_list.insert(*gray_set_it);
        vector <unsigned int> current_gray_reach_list;
        current_gray_reach_list.push_back(*gray_set_it);

        //set<unsigned int> current_reach_list;
        //current_reach_list.insert(*gray_set_it);
        vector <unsigned int> current_reach_list;
        //current_reach_list.push_back(*gray_set_it);

        //color label for DFS
        vector <unsigned int> color_label;
        color_label.assign(ff_obj.get_vertex_num(), 0); //white
        //current_reach_list.clear();

        //find reachable gray vertexes from v by BFS
        queue<unsigned int> examine_queue;
        examine_queue.push(*gray_set_it);

        while (!examine_queue.empty())
        {
            unsigned int cur_v = examine_queue.front();
            vector<edge>::iterator fanout_it;
            for (fanout_it = ff_obj.vertex_fanout[cur_v].begin(); 
                fanout_it != ff_obj.vertex_fanout[cur_v].end(); ++fanout_it)
            {
                struct edge e = *fanout_it;
                if (0 == color_label[e.out] && 
                        ff_obj.get_vertex_color(e.out) != 2) 
                {
                    color_label[e.out] = 1;

                    if (ff_obj.get_vertex_color(e.out) == 1) //end gray v
                    {
                        //current_gray_reach_list.insert(e.out);
                        if (find (current_gray_reach_list.begin(), 
                            current_gray_reach_list.end(), e.out) 
                            == current_gray_reach_list.end())
                        {
                            current_gray_reach_list.push_back(e.out);
                        }
                    }
                    else 
                    {
                        examine_queue.push(e.out);
                        //current_reach_list.insert(e.out);
                        //if (find (current_reach_list.begin(), 
                        //    current_reach_list.end(), e.out) 
                        //    == current_reach_list.end())
                        //{
                        //    current_reach_list.push_back(e.out);
                        //}
                    }
                }
            }
            examine_queue.pop();
        }
        
        sort(current_gray_reach_list.begin(), current_gray_reach_list.end());
        gray_reach_list.push_back(current_gray_reach_list);

        //sort(current_reach_list.begin(), current_reach_list.end()); 
        //full_reach_list.push_back(current_reach_list);
        //gray_reach_list.insert(current_gray_reach_list);
        //full_reach_list.insert(current_reach_list);
    }

    //set<set<unsigned int> >::iterator it1;
    //set<unsigned int >::iterator it2;
    vector < vector<unsigned int> >::iterator it1;
    vector <unsigned int >::iterator it2;

    for (it1 = gray_reach_list.begin(); it1 != gray_reach_list.end(); it1++)
    {
        for (it2 = (*it1).begin(); it2 != (*it1).end(); it2++)
        {
            if (it2 == (*it1).begin()) continue;
            prime_path_num++;
        }
    }

    ff_obj.edge_set = temp_edge_set;
    ff_obj.gen_fanins();
    ff_obj.gen_fanouts();
    cout<< "prime_path_num = " << prime_path_num <<endl;
}

void graph_partition::partition_refinement(class ff_graph& ff_obj)
{
    gen_gray_reach_list(ff_obj);

    vector <unsigned int> first_stage_fanout;

    vector <unsigned int>::iterator gray_set_it;
    for (gray_set_it = gray_set.begin(); 
            gray_set_it != gray_set.end(); gray_set_it++) 
    {
        unsigned int gray_v = *gray_set_it;
        vector<edge>::iterator fanout_it;
        for (fanout_it = ff_obj.vertex_fanout[gray_v].begin(); 
             fanout_it != ff_obj.vertex_fanout[gray_v].end(); ++fanout_it)
        {
            first_stage_fanout.push_back((*fanout_it).out);
        }
    }

    sort(first_stage_fanout.begin(), first_stage_fanout.end());
    for (unsigned int i = 0; i < first_stage_fanout.size() - 1; i++)
    {
        if (first_stage_fanout[i] == first_stage_fanout[i + 1])
        {
            if (ff_obj.get_vertex_color(first_stage_fanout[i]) == 0)
            {
                ff_obj.set_vertex_color(first_stage_fanout[i], 1);
                gray_set.push_back(first_stage_fanout[i]);
            }
        }
    }

    sort (gray_set.begin(), gray_set.end());

    /*vector <vector<unsigned int> >::iterator it1;
    vector <unsigned int >::iterator it2;

    vector <unsigned int> vertex_reach_num;
    vertex_reach_num.assign(ff_obj.get_vertex_num(), 0);

    int reach_num = 0;
    for (it1 = full_reach_list.begin(); it1 != full_reach_list.end(); it1++)
    {
        for (it2 = (*it1).begin(); it2 != (*it1).end(); it2++)
        {
            if (it2 == (*it1).begin()) continue;
            
            vertex_reach_num[(*it2)]++;
        }
    }

    unsigned int max_reach_num = vertex_reach_num[0];
    unsigned int max_index = 0;
    for (unsigned int i = 0; i < vertex_reach_num.size(); i++)
    {
        if (vertex_reach_num[i] > 1)
        {
            ff_obj.set_vertex_color(i, 1);
            gray_set.push_back(i);
        }
    }
    sort (gray_set.begin(), gray_set.end());*/



    gen_gray_reach_list(ff_obj);

    //cout<< "max_reach_num = " << max_reach_num <<endl;

    //ff_obj.set_vertex_color(max_index, 1);
    //gray_set.insert(max_index);

    //gen_gray_reach_list(ff_obj);
    /*set<unsigned int >::iterator gray_set_it;
    set<unsigned int >::iterator gray_set_end_it;

    //check gray vertex shared white vertex, not include S and D
    gray_set_it = gray_set.begin();
    gray_set_it++;
    gray_set_end_it = gray_set.end();
    gray_set_end_it--;
    for (; gray_set_it != gray_set_end_it; gray_set_it++) 
    {
        unsigned int gray_vertex = *gray_set_it;
        if (ff_obj.vertex_fanin[gray_vertex].empty()) //no fanin
        {
            vector<edge>::iterator fanout_it;
            for (fanout_it = ff_obj.vertex_fanout[gray_vertex].begin(); 
                fanout_it != ff_obj.vertex_fanout[gray_vertex].end(); ++fanout_it)
            {
                unsigned int fanout = (*fanout_it).out;
                if (ff_obj.get_vertex_color(fanout) == 0) 
                {
                    white_adjv_set.push_back(fanout);
                }
            }
        }
        else if (ff_obj.vertex_fanin[gray_vertex].size() == 1) 
        {
            unsigned int fanin = ff_obj.vertex_fanin[gray_vertex][0].in;
            if (ff_obj.get_vertex_color(fanin) == 0)
            {
                vector<edge>::iterator fanout_it;
                for (fanout_it = ff_obj.vertex_fanout[gray_vertex].begin(); 
                    fanout_it != ff_obj.vertex_fanout[gray_vertex].end(); ++fanout_it)
                {
                    unsigned int fanout = (*fanout_it).out;
                    if (ff_obj.get_vertex_color(fanout) == 0)
                    {
                        white_adjv_set.push_back(fanout);
                    }
                }
            }
        }
    }

    sort(white_adjv_set.begin(), white_adjv_set.end());

    //set repetitive white vertex gray
    for (unsigned int i = 0; i < white_adjv_set.size() - 1; i++)
    {
        if (white_adjv_set[i] == white_adjv_set[i + 1])
        {
            unsigned int new_gray_v = white_adjv_set[i];

            bool no_white_fanin_vertex = true;

            //check no white fan_in
            vector<edge>::iterator fanin_it;
            for (fanin_it = ff_obj.vertex_fanin[new_gray_v].begin(); 
            fanin_it != ff_obj.vertex_fanin[new_gray_v].end(); ++fanin_it)
            {
                unsigned int fanin = (*fanin_it).in;
                if (ff_obj.get_vertex_color(fanin) == 0)
                {
                    no_white_fanin_vertex = false;
                }

                vector<edge>::iterator fanout_it;
                for (fanout_it = ff_obj.vertex_fanout[fanin].begin(); 
                    fanout_it != ff_obj.vertex_fanout[fanin].end(); ++fanout_it)
                {
                    unsigned int fanout = (*fanout_it).out;
                    if (ff_obj.get_vertex_color(fanout) == 0 
                        && fanout != new_gray_v)
                    {
                        no_white_fanin_vertex = false;
                    }
                }
            }

            if (no_white_fanin_vertex == true)
            {
                ff_obj.set_vertex_color(new_gray_v, 1);
                //gray_set.insert(new_gray_v);
                vector<edge>::iterator fanin_it;
                for (fanin_it = ff_obj.vertex_fanin[new_gray_v].begin(); 
                    fanin_it != ff_obj.vertex_fanin[new_gray_v].end(); ++fanin_it)
                {
                    unsigned int new_black = (*fanin_it).in;
                    //only set gray as black not set white as black
                    if (ff_obj.get_vertex_color(new_black) == 1) 
                    {
                        ff_obj.set_vertex_color(new_black, 2); // set fanin black

                        //set new black adj v as gray
                        vector<edge>::iterator new_black_fanout_it;
                        for (new_black_fanout_it = ff_obj.vertex_fanout[new_black].begin(); 
                            new_black_fanout_it != ff_obj.vertex_fanout[new_black].end(); 
                            ++new_black_fanout_it)
                        {
                            //white then color
                            if (ff_obj.get_vertex_color((*new_black_fanout_it).out) == 0) 
                            {
                                ff_obj.set_vertex_color((*new_black_fanout_it).out, 1); 
                                //gray_set.insert((*new_black_fanout_it).out);
                            }
                        }

                        vector<edge>::iterator new_black_fanin_it;
                        for (new_black_fanin_it = ff_obj.vertex_fanin[new_black].begin(); 
                            new_black_fanin_it != ff_obj.vertex_fanin[new_black].end(); 
                            ++new_black_fanin_it)
                        {
                            //white then color
                            if (ff_obj.get_vertex_color((*new_black_fanin_it).in) == 0) 
                            {
                                ff_obj.set_vertex_color((*new_black_fanin_it).in, 1); 
                                //gray_set.insert((*new_black_fanin_it).in);
                            }
                        }
                    }
                }
            }
        }
    }

    gray_set.clear();

    unsigned int gray_num = 0;
    for (unsigned int i = 0; i < ff_obj.get_vertex_num(); i++)
    {
        if (ff_obj.get_vertex_color(i) == 1)
        {
            gray_num++;
            gray_set.insert(i);
        }
    }
    */
    //gen_gray_reach_list(ff_obj);
    
    //generate hypergraph for reduction
}

void graph_partition::subgraph_extraction(class ff_graph& ff_obj, 
                        char* partition_file)  
{
    FILE *fp_partition = fopen (partition_file, "r");

    if (fp_partition == NULL)
    {
        perror ("Error opening hgr.part.npart file");
    }
    
    rewind (fp_partition); 

    //all gates are white initially
    ff_obj.initial_vertex_color();

    vector <unsigned int> black_vertex;
    unsigned int vertex = 0;
    black_num = 0;

    while (!feof (fp_partition)) 
    {
        int part_num;
        fscanf(fp_partition,"%d\n", &part_num);

        //implementation note: 8 time i for partition number

#ifdef ISCAS89
        //partition parameter for ISCAS
        if (part_num < 65 && part_num > 0 && part_num % 8 == 0) 
#else
        //partition parameter for ITC
        if (part_num < 7 && part_num > 0 || part_num == 20) 
#endif 
        {
            if (vertex != 0 && vertex!= ff_obj.get_vertex_num() - 1)
            {
                black_vertex.push_back(vertex);
                ff_obj.set_vertex_color(vertex, 2);
                black_num ++;
            }
        }
        vertex++;
    }

    //set boundary black_v as gray
    vector<unsigned int>::iterator black_vertex_it;
    /*
    for (black_vertex_it = black_vertex.begin(); 
            black_vertex_it != black_vertex.end(); black_vertex_it++)
    {
        unsigned int v = *black_vertex_it;
        vector<edge>::iterator edge_it;
        for (edge_it = ff_obj.vertex_fanin[v].begin(); 
            edge_it != ff_obj.vertex_fanin[v].end(); ++edge_it)
        {
            unsigned int source = (*edge_it).in;

            if (ff_obj.get_vertex_color(source) == 0) //boundary
            {
                ff_obj.set_vertex_color(v, 1); // set gray
                break;
            }
        }

        for (edge_it = ff_obj.vertex_fanout[v].begin(); 
            edge_it != ff_obj.vertex_fanout[v].end(); ++edge_it)
        {
            unsigned int sink = (*edge_it).out;

            if (ff_obj.get_vertex_color(sink) == 0) //boundary
            {
                ff_obj.set_vertex_color(v, 1); // set gray 
            }
        }
    }

    black_num = 0;
    black_vertex.clear();

    for (unsigned int i = 0; i < ff_obj.get_vertex_num(); i++)
    {
        if (ff_obj.get_vertex_color(i) == 2)
        {
            black_vertex.push_back(i);
            black_num ++;
        }
    }*/

    cout << "black_num = "<< black_num << endl; 

    // set all adjacent vertexes gray
    
    for (black_vertex_it = black_vertex.begin(); 
            black_vertex_it != black_vertex.end(); black_vertex_it++)
    {
        unsigned int v = *black_vertex_it;
        vector<edge>::iterator edge_it;
        for (edge_it = ff_obj.vertex_fanin[v].begin(); 
            edge_it != ff_obj.vertex_fanin[v].end(); ++edge_it)
        {
            unsigned int source = (*edge_it).in;

            if (ff_obj.get_vertex_color(source) != 2)
            {
                ff_obj.set_vertex_color(source, 1); // set src vertex gray 
            }
        }

        for (edge_it = ff_obj.vertex_fanout[v].begin(); 
            edge_it != ff_obj.vertex_fanout[v].end(); ++edge_it)
        {
            unsigned int sink = (*edge_it).out;

            if (ff_obj.get_vertex_color(sink) != 2)
            {
                ff_obj.set_vertex_color(sink, 1); // set sink vertex gray 
            }
        }
    }

    /*
    *Implmentation notes: 
    * assume there exists 32 different gates in cell library 
    *
    t *= 32;*/

    // set src and dist gray
    ff_obj.set_vertex_color(0, 1);
    ff_obj.set_vertex_color(ff_obj.get_vertex_num() - 1, 1);

    unsigned int gray_num = 0;
    for (unsigned int i = 0; i < ff_obj.get_vertex_num(); i++)
    {
        if (ff_obj.get_vertex_color(i) == 1)
        {
            gray_num++;
            //gray_set.insert(i);
            gray_set.push_back(i);
        }
    }
    
    //partition_refinement(ff_obj);
    //gen_gray_reach_list(ff_obj);

    //relabeling vertexes without black ones and record the mapping information
    vector<unsigned int>::iterator tp_it;
    for (tp_it = ff_obj.tp_order.begin(); 
            tp_it != ff_obj.tp_order.end(); tp_it++)
    {
        if (ff_obj.get_vertex_color(*tp_it) != 2) //not black
        {
            mapping.push_back(*tp_it);
        }
    }

    //verify the mapping
    assert(black_num == ff_obj.get_vertex_num() - mapping.size());

    //relabel non-black edges and store
    vector <int> map_idx; //vertex's map idx
    map_idx.assign(ff_obj.get_vertex_num(), -1);

    //relabeling the vertex
    for (unsigned int i = 0; i < mapping.size(); i++)
    {
        // i is order
        map_idx[mapping[i]] = i;
    }

    /*int gray_edge_num = 0;
    for (unsigned int i = 0; i < ff_obj.edge_set.size(); i++)
    {
        struct edge e = ff_obj.edge_set[i];
        if (ff_obj.get_vertex_color(e.in) == 1 && 
            ff_obj.get_vertex_color(e.out) == 1)
        {
            gray_edge_num++;
        }        
    }*/

    //relabeling the edge
    non_black_edges.clear();
    vector<unsigned int>::iterator map_it;
    for (map_it = mapping.begin(); map_it != mapping.end(); map_it++)
    {
        unsigned int v = *map_it;
        vector<edge>::iterator edge_it;
        for (edge_it = ff_obj.vertex_fanout[v].begin(); 
            edge_it != ff_obj.vertex_fanout[v].end(); ++edge_it)
        {
            struct edge e = (*edge_it);

            // filter edge with gray in and out, do not send to cloud
            if (ff_obj.get_vertex_color(e.in) == 1 && 
                ff_obj.get_vertex_color(e.out) == 1)
            {
                continue;
            }

            e.in = map_idx[e.in];
            e.out = map_idx[e.out];

            if (e.in != -1 && e.out != -1)
            {
                non_black_edges.push_back(e);
            }
        }
    }
}

void graph_partition::write_black_graph(class ff_graph& ff_obj, char* benchmark)
{
    FILE *fp_color = fopen (benchmark, "w");

    if (fp_color == NULL) 
    {
        perror ("Error opening black graph file");
    }

    rewind(fp_color); 
    fprintf(fp_color,"                                                  \n");

    unsigned int black_edge = 0;

    for (unsigned int i = 0; i < ff_obj.edge_set.size(); i++)
    {
        struct edge e = ff_obj.edge_set[i];
        if (ff_obj.get_vertex_color(e.in) == 2 || 
            ff_obj.get_vertex_color(e.out) == 2)
        {
            fprintf(fp_color," %d %d\n", e.in, e.out);
            black_edge++;
        }

        //store edges with gray in out in local
        if (ff_obj.get_vertex_color(e.in) == 1 && 
            ff_obj.get_vertex_color(e.out) == 1)  
        {
            fprintf(fp_color," %d %d\n", e.in, e.out);
            black_edge++;
        }
    }
    
    rewind(fp_color);
    fprintf(fp_color," %d %d", black_num, black_edge);
    cout <<benchmark<< endl;
    cout <<"black_num= "<< black_num<< endl;
    cout <<"black_edge= "<< black_edge<< endl;
    cout << endl;

    fclose(fp_color);
}

void graph_partition::write_partition_graph(class ff_graph& ff_obj, 
                                    char* benchmark)
{
    //output file name
    char gp_file[50];
    char black_file[50];

    strcpy(gp_file, "../benchmark/BENCH/cloud/");
    strcpy(black_file, "../benchmark/BENCH/local/");

    strcat(gp_file, benchmark);
    strcat(black_file, benchmark);

    strcat(gp_file, "_p2.dag"); //phase two
    strcat(black_file, "_black_p2.dag"); //phase two

    write_black_graph(ff_obj, black_file);

    FILE *fp_gp = fopen (gp_file, "w");

    if (fp_gp == NULL) 
    {
        perror ("Error opening partition graph file");
    }

    //jump to file beginning
    rewind(fp_gp); 
    fprintf(fp_gp,"                                                  \n");

    //initial a temp edge struct
    struct edge e; 
    e.in = 0;
    e.out = 0;

    //implement phase 2 write
    unsigned int edge_num = non_black_edges.size();
    for (unsigned int i = 0; i < edge_num; i++)
    {
        struct edge e = non_black_edges[i];
        fprintf(fp_gp," %d %d\n", e.in, e.out);
    }

    rewind(fp_gp);
    //don't record ff number
    fprintf(fp_gp," %d %d %d", mapping.size(), -1, edge_num);

    cout << "outsourced vertex_num = " << mapping.size() <<endl;
    cout << "outsourced edge_num = " << edge_num <<endl;
    cout << endl;

    fclose(fp_gp);
}

void graph_partition::write_mapping(class ff_graph& ff_obj, char* benchmark)
{
    char map_file[50];
#ifndef ITC99
    strcpy(map_file, "../benchmark/BENCH/local/");
#else
    strcpy(map_file, "../benchmark/ITC99/local/");
#endif
    strcat(map_file, benchmark);
    strcat(map_file, "_p2.map"); //phase two

    FILE *fp_map = fopen (map_file, "w");

    if (fp_map == NULL)
    {
        perror ("Error opening map file");
    }
    
    rewind (fp_map); 
    for (unsigned int i = 0; i < mapping.size(); i++)
    {
        fprintf(fp_map," %d %d\n", mapping[i], 
                ff_obj.get_vertex_color(mapping[i]));
    }
    fclose(fp_map);
}

void graph_partition::write_delay(class ff_graph& ff_obj, char* benchmark)
{
    /*
     * Implementation notes:
     *  store black vertex delay locally and sent others to cloud;
     */
    
    //output file name
    char delay_file[50];
    strcpy(delay_file, "../benchmark/BENCH/cloud/");
    strcat(delay_file, benchmark);

    //record colored vertex
    char color_delay_file[50]; //black and gray vertex delay store in local
    strcpy(color_delay_file, "../benchmark/BENCH/local/");
    strcat(color_delay_file, benchmark);

    strcat(delay_file, "_p2.delay"); //phase two
    strcat(color_delay_file, "_color_p2.delay"); //phase two

    FILE *fp_delay = fopen (delay_file, "w");
    FILE *fp_color_delay = fopen (color_delay_file, "w");

    rewind (fp_delay); 
    fprintf(fp_delay," %d %d\n", mapping.size(), VARNUM); //total# white and gray

    rewind (fp_color_delay); 
    fprintf(fp_color_delay," %d %d\n", black_num, VARNUM);

    if (fp_delay == NULL || fp_color_delay == NULL) 
    {
        perror ("Error opening delay file");
    }

    //implement for phase 2

    /**
     * Implementation note: random seed should be shuffed. For simplicity, i+100
     */
    unsigned int gray_num = 0;

    for (unsigned int i = 0; i < ff_obj.tp_order.size(); i++)
    {
        unsigned color = ff_obj.get_vertex_color(i);
        if (color == 2) //black
        {
            fprintf(fp_color_delay," %d", i); //vertex index
            for (unsigned int j = 0; j < VARNUM; j++)
            {
                fprintf(fp_color_delay," %.9f", ff_obj.vertex_delay[i][j]); 
            }
            fprintf(fp_color_delay," \n"); 
        }
        else if (color == 1) //gray 
        {
            fprintf(fp_color_delay," %d", i);
            fprintf(fp_delay," %d %d", color, i + 100); // record seed
            for (unsigned int j = 0; j < VARNUM; j++)
            {
                fprintf(fp_color_delay," %.9f", ff_obj.vertex_delay[i][j]); 
                fprintf(fp_delay," %.9f", ff_obj.vertex_delay[i][j]); 
            }
            fprintf(fp_color_delay," \n");
            fprintf(fp_delay," \n"); 
            gray_num++;
        }
        else // white
        {
            fprintf(fp_delay," %d %d", color, i + 100); // record seed
            for (unsigned int j = 0; j < VARNUM; j++)
            {
                fprintf(fp_delay," %.9f", ff_obj.vertex_delay[i][j]); 
            }
            fprintf(fp_delay," \n"); 
        }
    }

    cout << "gray_num= " << gray_num << endl;

    fclose(fp_delay);
    fclose(fp_color_delay);
}

void graph_partition::ip_protection(class ff_graph& ff_obj, char* benchmark)
{
    char partition_file[50];
    strcpy(partition_file, "../benchmark/BENCH/partition/");
    strcat(partition_file, benchmark);
    strcat(partition_file, ".hgr.part.80"); //step 10 nparts

    //preassign gray and black to one partition for refinement
    /*char fix_file[50]; 
    strcpy(fix_file, "../benchmark/BENCH/partition/");
    strcat(fix_file, benchmark);
    strcat(fix_file, ".fix"); //phase two
    //partition_to_fix(ff_obj, partition_file, fix_file);*/

    subgraph_extraction(ff_obj, partition_file);

    write_partition_graph(ff_obj, benchmark);
    write_delay(ff_obj, benchmark);
    write_mapping(ff_obj, benchmark);
}